// Parser.cpp

// Implementacia triedy na parsovanie prikazov

#include "Common/Protocol/Parser.h"
#include "Common/Protocol/ParseStrings.h"
//#include "Common/Protocol/ParseStructs.h"
//#include "Common/World/ParamStructs.h"
#include "Common/Tasks.h"
#include "Common/Support/Debug.h"
#include <stdio.h>
#include <string.h>

// MAKRA

/*
	Ak je "name" totozne s "pattern", tak sa do params.pattern priradi celociselna "value" a pokracuje sa.
	Toto pomocne makro sa pouziva v ParseServerParam.
	\param name - retazec, ktory sa porovnava s vzorom (pattern)
	\param pattern - vzor pre porovnavanie
	\param value - retazec obsahujuci celociselnu hodnotu
	\param params - ServerParams, do ktorych priradujeme
	\param counter - celociselne pocitadlo, ktore sa inkrementuje po priradeni
*/
#define IF_EQUAL_ASSIGN_INT(name, pattern, value, params, counter) \
	if(strcmp(name, #pattern) == 0) \
	{ \
		int tmp; \
		if(sscanf(value, "%i", &tmp) != 1) \
		{ \
			WARNN("could not convert %s to int.", value); \
			return 1; \
		} \
		params.pattern = tmp; \
		counter++; \
		continue; \
	}

/*	
	Ak je "name" totozne s "pattern", tak sa do params.pattern priradi desatinne cislo "value" a pokracuje sa.
	Toto pomocne makro sa pouziva v ParseServerParam.
	\param name - retazec, ktory sa porovnava s vzorom (pattern)
	\param pattern - vzor pre porovnavanie
	\param value - retazec obsahujuci desatinnu hodnotu
	\param params - ServerParams, do ktorych priradujeme
	\param counter - celociselne pocitadlo, ktore sa inkrementuje po priradeni
*/
#define IF_EQUAL_ASSIGN_FLOAT(name, pattern, value, params, counter) \
	if(strcmp(name, #pattern) == 0) \
	{ \
		float tmp; \
		if(sscanf(value, "%f", &tmp) != 1)\
		{ \
			WARNN("could not convert %s to float.", value); \
			return 1; \
		} \
		params.pattern = tmp; \
		counter++; \
		continue; \
	}

/*	
	Ak je "name" totozne s "pattern", tak sa do params.pattern priradi retazcova hodnota "value" a pokracuje sa.
	Toto pomocne makro sa pouziva v ParseServerParam.
	\param name - retazec, ktory sa porovnava s vzorom (pattern)
	\param pattern - vzor pre porovnavanie
	\param value - retazec obsahujuci retazcovu hodnotu
	\param params - ServerParams, do ktorych priradujeme
	\param counter - celociselne pocitadlo, ktore sa inkrementuje po priradeni
*/
#define IF_EQUAL_ASSIGN_STRING(name, pattern, value, params, counter) \
	if(strcmp(name, #pattern) == 0) \
	{ \
		strcpy(params.pattern, value); \
		counter++; \
		continue; \
	}

// KONIEC MAKIER

// Konstruktor
Parser::Parser() : Module("Parser", "Parser module", "v0.1.003")
{
	teamName[0] = 0;
	synchro = NULL;

#ifndef COACH
	realSide = LEFT;
#endif

	Main::GetInstance()->RegisterModule(this, MESSAGE_PROCESS_TASK);
}

// Vrati interfejs pre zadanu ulohu
Interface* Parser::GetInterface(int task) const
{
	return (MsgProcessInterface*) this;
}

// Inicializovanie parsera
// \param argc - pocet argumentov programu
// \param argv - samotne argumenty programu
// vracia stavy: INIT_OK alebo INIT_ERR
int Parser::Init(int argc, char** argv, int)
{
#ifdef COACH
	synchro = (CoachSynchronizationInterface*) Main::GetInstance()->GetInterface(SYNCHRONIZATION_TASK);
#else
	synchro = (SynchronizationInterface*) Main::GetInstance()->GetInterface(SYNCHRONIZATION_TASK);
	memory = (MemoryInterface*) Main::GetInstance()->GetInterface(MEMORY_TASK);
	tactics = (TacticsInterface*) Main::GetInstance()->GetInterface(TACTICS_TASK);
#endif

	return INIT_OK;
}

// Parsuje spravu obdraznu od servera. Tato metoda vola vsetky ostatne parsovacie metody.
// \param msg - sprava na zparsovanie
// vracia 0 v pripade uspechu, inak chbyu
int Parser::ParseMsg(const char* msg)
{
	static char message[BUFFERSIZE];
	
	// Sprava zacina vzdy zatvorkou, ktoru potrebujeme odstranit
	if(!ParseBracket(msg, message))
	{
		WARNN("message not starting with a bracket");
		return 1; // chyba
	}

	static char typ[64];

	// Prve slovo hned za odstranenou zatvorkou by malo urcovat typ spravy
	const char* nextStr = ParseObj(message, typ);

	// Ak sprava nema dalsie argumenty, pravdepodobne doslo ku chybe
	if(nextStr == NULL) 
		return 1; // chyba

	int success = 0;

	// Podla typu spravy ju dalej parsujeme 
#ifdef COACH
	if(strcmp(typ, "see_global") == 0)
		success = ParseSeeGlobal(nextStr);
#else  // COACH
	if(strcmp(typ, "sense_body") == 0)
		success = ParseSenseBody(nextStr);
	else if(strcmp(typ, "see") == 0) 
		success = ParseSee(nextStr);
#endif // COACH
	else if(strcmp(typ, "ok") == 0) 
		success = ParseOk(nextStr);
	else if(strcmp(typ, "hear") == 0)
		success = ParseHear(nextStr);
	else if(strcmp(typ, "change_player_type") == 0)
		success = ParseChangePT(nextStr);
	else if(strcmp(typ, "player_type") == 0)
		success = ParsePlayerType(nextStr);
	else if(strcmp(typ, "init") == 0) 
		success = ParseInit(nextStr);
	else if(strcmp(typ, "server_param") == 0)
		success = ParseServerParam(nextStr);
	else if(strcmp(typ, "player_param") == 0)
		success = ParsePlayerParam(nextStr);
	else if(strcmp(typ, "score") == 0)
		success = ParseScore(nextStr);

	else if(strcmp(typ, "error") == 0)
		WARNN("server reports \"%s\"", msg);
	else if(strcmp(typ, "warning") == 0)
		WARNN("server reports \"%s\"", msg);
	else if(strcmp(typ, "can't") == 0)
		WARNN("server reports \"%s\"", msg);
	else WARNN("not parsed: %s", msg);

	return success;
}

// Nastavi skutocnu stranu ihriska
void Parser::SetRealSide(RealSide rs)
{
#ifndef COACH
	realSide = rs;
#endif // COACH
}

// Nastavi meno timu
void Parser::SetTeamName(const char* name)
{
	strncpy(teamName, name, 32);
	teamName[31] = 0;
}

/*	Parsovanie objektu v zatvorkach
	
	Priklad:
	Vstup (str): "(xxx aaa (y x)) xx"
	Vystup (out): "xxx aaa (y x)"
	Vrati pointer na prvy znak " xx"

	\param str - string na parsovanie
	\param out - buffer, kde sa skopiruje text vo vnutri zatvoriek
	vracia sa pointer na prvy znak za uzatvaraciou zatvorkou alebo NULL v pripade chyby
*/
const char* Parser::ParseBracket(const char* str, char* out)
{
	// Nemame co alebo kam parsovat
	if(str == NULL || out == NULL)
		return NULL;

	// Pocet neparovych zatvoriek
	int brackets = 1;

	// Hlada sa prva zatvorka
	while(*str != '\0' && *str != '(')
		str++;
	if(*str == '\0')
		return NULL;

	// Sme vo vnutri uvodzoviek?
	bool quotes = false;

	while(brackets > 0)
	{
		str++;
		switch(*str)
		{
			case '(':
				if(!quotes) 
					brackets++;
				break;
			case ')':
				if(!quotes)
					brackets--;
				break;
			case 0:
				return NULL;
			case '\"': // uvodzovka
				quotes = !quotes;
				break;
			default:
				break;
		}

		if(brackets > 0)
		{
			*out = *str;
			out++;
		}
		// Mame uzatvorene vsetky zatvorky
		else
		{
			*out = '\0';
		}
	}

	str++;
	return str;
}

/*	Parsovanie objektu
	
	Priklad:
	Vstup (str): "xxx aaa (y x)"
	Vystup (out): "xxx"
	Vrati pointer na prvy znak " aaa (y x)"

	\param str - string na parsovanie
	\param out - buffer, kde sa skopiruje prvy objekt
	vracia sa pointer na prvy znak za objektom alebo NULL v pripade chyby
*/
const char* Parser::ParseObj(const char* str,char* out)
{
	// Nemame co alebo kam parsovat
	if(str == NULL || out == NULL) 
		return NULL;

	// Odstran veduce biele znaky
	while(*str == ' ')
		str++;
	if(*str == 0) 
		return NULL;

	// V pripade uvodzoviek, skopiruj vsetky znaky do buffera
	if(*str == '\"')
	{
		str++;
		while(*str != '\"')
		{
			*out = *str;
			if(*str == '\0')
				return NULL;
			out++;
			str++;
		}
		str++;
	}
	else
		// Inak kopirujeme znaky do buffera iba po najblizsiu medzeru
		while(*str != '\0' && *str != ' ')
		{
			*out = *str;
			out++;
			str++;
		}

	*out = '\0';
	return str;
}

// Parsuje sa pocuta sprava
// \param msg - sprava na parsovanie
// vracia sa 0 v pripade uspechu, inak chyba
int Parser::ParseHear(const char* msg)
{
	//DBGN("hear %s", msg);

/*	Hrac moze pocut spravu od:
	- rozhodcu: (hear TIME referee PLAY_MODE)
	- offline kouca: (hear TIME coach "MSG")
	- online kouca: (hear TIME online_coach_{left|right} TIME_RECEIVED_BY_SERVER CLANG_MSG)
	- svoju vlastnu spravu: (hear TIME self "MSG")	
	- ostatnych spravy: (hear TIME DIRECTION <opp|our UNUM> "MSG") 
	- ostatnych spravy (nepouzivany sposob): (hear TIME <opp|our UNUM>)
     
	Rozhodca moze pouct spravu od:
	- rozhodcu: (hear TIME referee PLAY_MODE)
	- offline kouca: (hear TIME coach "MSG")
	- od hracov: (hear TIME (p "TEAMNAME" UNUM) "MSG") 
*/    
	 
	static char tmp[BUFFERSIZE];

	// Parsovany cas
	int time;

	msg = ParseObj(msg, tmp);
	
	// Sprava musi nieco obsahovat
	if(msg == NULL)
		return 1;

	// Prvy parameter je vzdy cas
	if(sscanf(tmp, "%d", &time) != 1)
		return 1;

	// Samotne parsovanie spravy
#ifdef COACH
	// Kouch dostava spravy od hracov v zatvorkach
	if(msg[1] == '(')
		// Sprava od hraca
		msg = ParseBracket(msg, tmp);
	else
		// Sprava od rozhodcu alebo offline kouca
		msg = ParseObj(msg, tmp);
#else // COACH
	// Hracova sprava nie je v zatvorkach
	msg = ParseObj(msg, tmp);
#endif // COACH

	// Sprava musi nieco obsahovat
	if(msg == NULL)
		return 1;

	// Kto poslal danu spravu?

	// Ak rozhodca, tak ten posiela spravy iba k hracim modom
	if(strcmp("referee", tmp) == 0)
	{
		msg = ParseObj(msg, tmp);
		if(msg == NULL)
			return 1;

		PlayMode mode = ParsePlayMode(tmp);

		if(mode != PM_Null && synchro != NULL)
			synchro->OnChangeMode(time, mode);
	}
	// Vlastne spravy ingnorujem
	else if(strcmp("self", tmp) == 0)
	{
	
	}
	
#ifndef COACH
	else if(strcmp("online_coach_left", tmp) == 0) 
	{
//		msg = ParseObj(msg, tmp); // time
		CLangParseMsg(msg);
	}
	else if(strcmp("online_coach_right", tmp) == 0) 
	{
//		msg = ParseObj(msg, tmp); // time
		CLangParseMsg(msg);
	}
#endif // COACH
	else if(strcmp("coach", tmp) == 0)
	{
	
	}
	else
	{
		static char playerMsg[BUFFERSIZE];

#ifdef COACH
		// Sprava od hraca koucovi - (p "TEAMNAME" UNUM) "MSG"
		// V tmp mame obsah hornej zatvorky, v msg zvysok
		static char tmp2[BUFFERSIZE];
		
		// playerMsg je pocuta sprava
		msg = ParseObj(msg, playerMsg);
		if(msg == NULL)
			return 1;

		// tmp2 by malo byt "p"
		msg = ParseObj(tmp, tmp2);
		if(msg == NULL || strcmp(tmp2, "p") != 0)
			return 1;

		// Nazov timu by mal byt v tmp
		msg = ParseObj(msg, tmp);
		if(msg == NULL)
			return 1;

		// Cislo dresu by malo byt v tmp2
		msg = ParseObj(msg, tmp2);
		if(msg == NULL)
			return 1;
		
		int uniformNumber = atoi(tmp2);
		
		if(uniformNumber < 1 || uniformNumber > 11)
			return 1;

		bool ourTeam = (strcmp(teamName, tmp) == 0);
		//DBGN("At %i: %s player %i says %s.", time, ourTeam ? "our" : "their", uniformNumber, playerMsg);

		if(synchro != NULL)
			synchro->OnHear(time, ourTeam, uniformNumber, playerMsg);
#else  // COACH 
		
		// Sprava od hraca - DIRECTION <opp|our UNUM> "MSG"
		// Ignorujeme druhy variant spravy - <opp|our UNUM>
		float direction;
		if(sscanf(tmp, "%f", &direction) != 1)
			return 1;
		
		// Tim nas alebo superov ulozime do tmp
		msg = ParseObj(msg, tmp);
		if(msg == NULL)
			return 1;

		int uniformNumber = 0;
		bool ourTeam = (strcmp("our", tmp) == 0);
		
		// Ignorujeme superove spravy
		if(ourTeam)
		{
			// Cislo dresu ulozime do tmp
			msg = ParseObj(msg, tmp);
			
			if(msg == NULL)
				return 1;
			uniformNumber = atoi(tmp);
			
			if(uniformNumber < 1 || uniformNumber > 11)
				return 1;
		}

		// Skutocna sprava
		msg = ParseObj(msg, playerMsg);
		if(msg == NULL)
			return 1;

		//DBGN("At %i: %s player %i (dir %f) says %s.", time, ourTeam ? "our" : "their", uniformNumber, direction, playerMsg);

		if(synchro != NULL)
			synchro->OnHear(time, direction, ourTeam, uniformNumber, playerMsg);
#endif // COACH
	}

	return 0;
}

// Parsuje sa sprava na zmenu typu hracov
int Parser::ParseChangePT(const char* msg)
{
	unsigned num;
	int type;
	static int itWasWritten = 0;

	switch(sscanf(msg, " %u %d", &num, &type))
	{
	// Druhy tim	
	case 1:				
			return 0;
	// Nas tim	
	case 2:					
		if(synchro != NULL)
			synchro->OnChangePlayerType(num, type);
//-ZK			
		if(itWasWritten < 6) 	
		{
			TXTN("CHANGE TYPE %d, %d", num, type);
			itWasWritten++;
		}	
//-ZK/
		return 0;
	// Chyba
	default:				
		return 1;
	}
}

// Parsovanie inicializacnej spravy
// \param msg - sprava na parsovanie
// vracia sa 0 v pripade uspechu, inak chyba
int Parser::ParseInit(const char* msg)
{
	char side;

#ifdef COACH
	if(sscanf(msg, " %c", &side) != 1)
		return 1;
	
	// Premenuje sa debug subor na identifikovanie kouca
	char id[64];
	sprintf(id, "c__%c", side);
	DBG_SET_ID(id);

	// Spatne volanie
	if(synchro != NULL)
		synchro->OnInit(side == 'l');
#else
	int num;
	char mode[32];

	if(sscanf(msg, " %c %d %s", &side, &num, mode) != 3)
		return 1;

	if(side == 'l')
		realSide = LEFT;
	else if(side == 'r')
		realSide = RIGHT;
	else 
		return 1;

	// Premenuje sa debug subor na zobrazenie timu a cisla hraca
#ifdef GOALIE
	char type = 'g';
#else
	char type = 'p';
#endif

	char id[64];
	sprintf(id, "%c%02d%c", type, num, side);
	DBG_SET_ID(id);

	// Spatne volanie
	if(synchro != NULL)
		synchro->OnInit(realSide, num, ParsePlayMode(mode));
#endif

	return 0;
}

// Parsovanie spravy ok ako odozvu zo servera
// Napr.: "ok eye on", "ok teamnames ...", "ok change_player_type ..."
// \param msg - sprava na parsovanie
// vracia sa 0 v pripade uspechu, inak chyba
int Parser::ParseOk(const char* msg)
{
#ifdef COACH
	if(strncmp(msg, " team_graphic ", 14) == 0)
	{
		int x, y;

		if(sscanf(msg, " team_graphic %d %d", &x, &y) == 2)
			synchro->OnOkTeamGraphic(x, y);
	}
#endif // COACH

	return 0;
}

// Funkcia parsuje parametre hracov, ktore sa posielaju po (server_param)
// \param msg - sprava na parsovanie
// v pripade uspechu sa vracia 0, inak chyba
int Parser::ParsePlayerParam(const char* msg)
{
	static PLAYER_PARAMS params;
	static char item[64], name[32], value[32];
	int counter = 0;

//	DBGN("Parsing player_param");

	while(true)
	{
		if(msg == NULL)
			return 1;

		msg = ParseBracket(msg, item);
		// Uz ziadne parametre, ale toto je stale Ok
		if(msg == NULL)
			break;
		
//		DBGN("1: %s", item);

		const char* tmp = ParseObj(item, name);
		if(tmp == NULL)
			return 1;
//		DBGN("2: %s", name);
		
		tmp = ParseObj(tmp, value);
		if(tmp == NULL)
			return 1;
//		DBGN("3: %s", value);
 
		IF_EQUAL_ASSIGN_INT(name, player_types, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, pt_max, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, random_seed, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, subs_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, dash_power_rate_delta_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, dash_power_rate_delta_min, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_max_delta_factor, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_min_delta_factor, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, extra_stamina_delta_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, extra_stamina_delta_min, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, inertia_moment_delta_factor, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kick_rand_delta_factor, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kickable_margin_delta_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kickable_margin_delta_min, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, new_dash_power_rate_delta_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, new_dash_power_rate_delta_min, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, new_stamina_inc_max_delta_factor, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_decay_delta_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_decay_delta_min, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_size_delta_factor, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_speed_max_delta_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_speed_max_delta_min, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, stamina_inc_max_delta_factor, value, params, counter)

		WARNN("unknown player parameter: %s\n", name);
	}
//-ZK 
	if(counter > PLAYER_PARAMS_COUNT)
	{
		WARNN("PLAYER_PARAMS_COUNT is too low, set it to %i\n", counter);
	}
	else if(counter < PLAYER_PARAMS_COUNT)
	{
		WARNN("server did not provide all needed player parameters (%i missing)\n",
			PLAYER_PARAMS_COUNT - counter);
	}
//-ZK/
	if(synchro != NULL)
		synchro->OnPlayerParam(&params);

	return 0;
}

// Funkcia parsuje typy hracov, ktore sa posielaju po (player_param)
// \param msg - sprava na parsovanie
// v pripade uspechu sa vracia 0, inak chyba
int Parser::ParsePlayerType(const char* msg)
{
	static PLAYER_TYPE params;
	static char item[64], name[32], value[32];
	int counter = 0;

//	DBGN("Parsing player_type");

	// (id) by malo byt ako prve
	msg = ParseBracket(msg, item);
	if(msg == NULL)
		return 1;

	const char* tmp = ParseObj(item, name);
	if(tmp == NULL)
		return 1;
		
	tmp = ParseObj(tmp, value);
	if(tmp == NULL)
		return 1;

	int id;
	if(strcmp(name, "id") != 0)
		return 1;
	if(sscanf(value, "%i", &id) != 1)
		return 1;

//	DBGN("ID: %i", id);

	// Parsujeme zvysok spravy
	while(true)
	{
		if(msg == NULL)
			return 1;

		msg = ParseBracket(msg, item);
		
		// Uz nie su ziadne dalsie parametre, ale to je ok
		if(msg == NULL)
			break;		
//		DBGN("1: %s", item);

		const char* tmp = ParseObj(item, name);
		if(tmp == NULL)
			return 1;
//		DBGN("2: %s", name);
		
		tmp = ParseObj(tmp, value);
		if(tmp == NULL)
			return 1;
//		DBGN("3: %s", value);

		IF_EQUAL_ASSIGN_FLOAT(name, player_speed_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, stamina_inc_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_decay, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, inertia_moment, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, dash_power_rate, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_size, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kickable_margin, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kick_rand, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, extra_stamina, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_min, value, params, counter)

		WARNN("unknown player type parameter: %s\n", name);
	}
//-ZK 
	if(counter > PLAYER_TYPE_PARAMS_COUNT)
	{
		WARNN("PLAYER_TYPE_PARAMS_COUNT is too low, set it to %i\n", counter);
	}
	else if(counter < PLAYER_TYPE_PARAMS_COUNT)
	{
		WARNN("server did not provide all needed player type parameters (%i missing)\n",
			PLAYER_TYPE_PARAMS_COUNT - counter);
	}
//-ZK/
	params.id = id;
	if(synchro != NULL) synchro->OnPlayerType(id, &params);
	return 0;
}

// Ziska herny mod z parsovaneho vstupneho retazca
// \pram str - herny mod ako retazec
// vracia sa herny mod ako cislo (PM_Null pre neznamy herny mod)

PlayMode Parser::ParsePlayMode(const char* str)
{
	PLAY_MODE_TRANSLATION *translation;
	
#ifdef COACH
	translation = PLAY_MODES_LEFT;
#else // COACH
	if(realSide == LEFT)
		translation = PLAY_MODES_LEFT;
	else
		translation = PLAY_MODES_RIGHT;
#endif // COACH

	for(int i = PM_Null + 1; i < PM_MAX; i++)
	{
		int length = strlen(translation[i].string);

		// We may get things like "goal_l_3" here, so we have to use strncmp.
		if(strncmp(str, translation[i].string, length) == 0)
		{
			return translation[i].mode;
		}
	}

	WARNN("unknown play mode: %s", str);

	return PM_Null;
}

// Parsuje spravu so stavom skore
// \param msg - sprava na parsovanie
// vracia sa 0 v pripade uspechu, inak chyba
int Parser::ParseScore(const char* msg)
{
	int time;
	unsigned ourScore, theirScore;
	
	if(sscanf(msg, " %d %d %d", &time, &ourScore, &theirScore) != 3)
	{
		return 1;
	}

	if(synchro != NULL) synchro->OnScore(time, ourScore, theirScore);

	return 0;
}

// Parsuje typy vlajok zo spravy see
// \param str - retazec na parsovanie
// vracia sa typ vlajky alebo ziadna ak nastane chyba

FlagType Parser::ParseSeeFlagType(const char* str)
{
	LANDMARK_TRANSLATION *translation;
	
#ifdef COACH
	translation = LANDMARKS_LEFT;
#else // COACH
	if(realSide == LEFT)
		translation = LANDMARKS_LEFT;
	else
		translation = LANDMARKS_RIGHT;
#endif // COACH

	for(int i = NoLandmark + 1; i < FLAG_MAX; i++)
	{
		int length = strlen(str);

		if(strncmp(&str[1], translation[i].string, length) == 0)
		{
			return translation[i].landmark;
		}
	}

	WARNN("unknown landmark: %s", str);
	return NoLandmark;
}

// // Parsuje videnu znacku a naplni strukturu LANDMARK
// \param str - cokolvek je za popisom znacky
// \param landmark - struktura LANDMARK, kde ukladame parsovane informacie
// vracia sa 0 v pripade uspechu, inak chyba 
int Parser::ParseSeeLandmark(const char* str, LANDMARK* landmark)
{
	// Parsuje sa pozicia a stav znacky
	float tmp[4];
	int rval = sscanf(str, " %f %f %f %f", &tmp[0], &tmp[1], &tmp[2], &tmp[3]);

	// Podla poctu parametrov o hracovi sa rozhodne, co poskytol server
	landmark->valid = landmark->distValid = landmark->changesValid = false;
	
	switch(rval)
	{
		case 1:
			landmark->dir	= tmp[0];
			break;

		case 2:
			landmark->dist	= tmp[0];
			landmark->dir	= tmp[1];
			landmark->distValid = true;
			break;

		case 4: // Toto vyuziva kouc v ParseSeeGlobal(...)
			landmark->dist	= tmp[0];
			landmark->dir	= tmp[1];
			landmark->distChange	= tmp[2];
			landmark->dirChange		= tmp[3];
			landmark->distValid		= true;
			landmark->changesValid	= true;
			break;

		default:
			return 1;
	}

	landmark->valid = true;
	return 0;
}

// Parsuje videneho hraca a naplni strukturu AGENT
// \param str1 - cokolvek ostalo za popisom objektu po odparsovani mena timu 
// \param str2 - cokolvek za popisom objektu
// \param agent - struktura AGENT, kde ukladame parsovane informacie
// vracia sa 0 v pripade uspechu, inak chyba
int Parser::ParseSeePlayer(const char* str1, const char* str2, AGENT* agent)
{
//	DBGN("parsing player: \"%s\" \"%s\"", str1, str2);
	
	static char tag1[32];

	// Parsujeme informaciu o drese
	int rval = sscanf(str1, " %d %s", &agent->uniformNumber, tag1);

	if(rval <= 0)
		agent->uniformNumber = 0;
	else if(rval == 1)
		agent->goalie = false;
	else if(rval == 2 && strcmp("goalie", tag1) == 0)
		agent->goalie = true;
	else return 1;

	// Parsujeme poziciu a stav hraca
	float tmp[7];
	rval = sscanf(str2, " %f %f %f %f %f %f %f", 
		&tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5], &tmp[6]);

	// Podla poctu parametrov hraca sa rozhodne, co poskytol server
	agent->valid = agent->distValid = agent->changesValid = agent->facingValid = agent->armValid = false;
	
	switch(rval)
	{
		case 1:
			agent->dir = tmp[0];
			break;

		case 2:
			agent->dist		= tmp[0];
			agent->dir		= tmp[1];
			agent->distValid = true;
			break;

		case 3:
			agent->dist		= tmp[0]; 
			agent->dir		= tmp[1];
			agent->armDir	= tmp[2];
			agent->distValid = true;
			agent->armValid = true;
			break;

		case 4:
			agent->dist			= tmp[0];
			agent->dir			= tmp[1];
			agent->distChange	= tmp[2];
			agent->dirChange	= tmp[3];
			agent->distValid	= true;
			agent->changesValid = true;
			break;

		case 6: // Toto vyuziva kouc v ParseSeeGlobal(...)
			agent->dist				= tmp[0];
			agent->dir				= tmp[1];
			agent->distChange		= tmp[2];
			agent->dirChange		= tmp[3];
			agent->bodyFacingDir	= tmp[4];
			agent->headFacingDir	= tmp[5];
			agent->distValid		= true;
			agent->changesValid		= true;
			agent->facingValid		= true;
			break;

		case 7:
			agent->dist				= tmp[0];
			agent->dir				= tmp[1];
			agent->distChange		= tmp[2];
			agent->dirChange		= tmp[3];
			agent->bodyFacingDir	= tmp[4];
			agent->headFacingDir	= tmp[5];
			agent->armDir			= tmp[6];
			agent->distValid		= true;
			agent->changesValid		= true;
			agent->facingValid		= true;
			agent->armValid			= true;
			break;
		
		default:
			return 1;
	}

	agent->valid = true;
	return 0;
}

// Parsuje parametre servera po sprave (init)
// \param msg - sprava na parsovanie
// vracia sa 0 v pripade uspechu, inak chyba
int Parser::ParseServerParam(const char* msg)
{
	static SERVER_PARAMS params;
	static char item[64], name[32], value[32];
	int counter = 0;

//	DBGN("Parsing server_param");

	while(true)
	{
		if(msg == NULL)
			return 1;

		msg = ParseBracket(msg, item);
		
		// Toto je ok, aj ked uz nie su dalsie ziadne parametre
		if(msg == NULL)
			break;		
//		DBGN("1: %s", item);

		const char* tmp = ParseObj(item, name);
		if(tmp == NULL)
			return 1;
//		DBGN("2: %s", name);
		
		tmp = ParseObj(tmp, value);
		if(tmp == NULL)
			return 1;
//		DBGN("3: %s", value);

		/*
		 * Prvy riadok nasledovneho podla makra znamena nasledovne:
		 *
	 		if(strcmp(name, "catch_ban_cycle") == 0)
			{
				int tmp;
				if(sscanf(value, "%i", &tmp) != 1)
				{
					WARNN("could not convert %s to int.", value);
					return 1;
				}
				params.catch_ban_cycle = tmp;
				counter++;
				continue;
			}
		 */

		IF_EQUAL_ASSIGN_INT(name, catch_ban_cycle, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, clang_advice_win, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, clang_define_win, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, clang_del_win, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, clang_info_win, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, clang_mess_delay, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, clang_mess_per_cycle, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, clang_meta_win, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, clang_rule_win, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, clang_win_size, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, coach_port, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, drop_ball_time, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, freeform_send_period, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, freeform_wait_period, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, game_log_compression, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, game_log_version, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, goalie_max_moves, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, half_time, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, hear_decay, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, hear_inc, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, hear_max, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, max_goal_kicks, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, olcoach_port, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, point_to_ban, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, point_to_duration, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, port, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, recv_step, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, say_coach_cnt_max, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, say_coach_msg_size, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, say_msg_size, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, send_step, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, send_vi_step, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, sense_body_step, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, simulator_step, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, slow_down_factor, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, start_goal_l, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, start_goal_r, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, synch_micro_sleep, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, synch_offset, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, tackle_cycles, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, text_log_compression, value, params, counter)
		IF_EQUAL_ASSIGN_STRING(name, game_log_dir, value, params, counter)
		IF_EQUAL_ASSIGN_STRING(name, game_log_fixed_name, value, params, counter)
		IF_EQUAL_ASSIGN_STRING(name, landmark_file, value, params, counter)
		IF_EQUAL_ASSIGN_STRING(name, log_date_format, value, params, counter)
		IF_EQUAL_ASSIGN_STRING(name, text_log_dir, value, params, counter)
		IF_EQUAL_ASSIGN_STRING(name, text_log_fixed_name, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, coach, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, coach_w_referee, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, old_coach_hear, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, wind_none, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, wind_random, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, back_passes, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, forbid_kick_off_offside, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, free_kick_faults, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, fullstate_l, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, fullstate_r, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, game_log_dated, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, game_log_fixed, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, game_logging, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, log_times, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, profile, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, proper_goal_kicks, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, record_messages, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, send_comms, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, synch_mode, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, team_actuator_noise, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, text_log_dated, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, text_log_fixed, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, text_logging, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, use_offside, value, params, counter)
		IF_EQUAL_ASSIGN_INT(name, verbose, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, audio_cut_dist, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, ball_accel_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, ball_decay, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, ball_rand, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, ball_size, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, ball_speed_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, ball_weight, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, catch_probability, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, catchable_area_l, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, catchable_area_w, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, ckick_margin, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, control_radius, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, dash_power_rate, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_inc, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_inc_thr, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_dec, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_dec_thr, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_init, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, effort_min, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, goal_width, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, inertia_moment, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kick_power_rate, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kick_rand, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kick_rand_factor_l, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kick_rand_factor_r, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, kickable_margin, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, maxmoment, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, maxneckang, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, maxneckmoment, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, maxpower, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, minmoment, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, minneckang, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, minneckmoment, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, minpower, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, offside_active_area_size, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, offside_kick_margin, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_accel_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_decay, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_rand, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_size, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_speed_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, player_weight, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, prand_factor_l, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, prand_factor_r, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, quantize_step, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, quantize_step_l, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, recover_dec, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, recover_dec_thr, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, recover_min, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, slowness_on_top_for_left_team, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, slowness_on_top_for_right_team, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, stamina_inc_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, stamina_max, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, stopped_ball_vel, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, tackle_back_dist, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, tackle_dist, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, tackle_exponent, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, tackle_power_rate, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, tackle_width, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, visible_angle, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, visible_distance, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, wind_ang, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, wind_dir, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, wind_force, value, params, counter)
		IF_EQUAL_ASSIGN_FLOAT(name, wind_rand, value, params, counter)

		WARNN("unknown server parameter: %s\n", name);
	}
//-ZK 
	if(counter > SERVER_PARAMS_COUNT)
	{
		WARNN("SERVER_PARAMS_COUNT is too low, set it to %i\n", counter);
	}
	else if(counter < SERVER_PARAMS_COUNT)
	{
		WARNN("server did not provide all needed parameters (%i missing)\n",
			SERVER_PARAMS_COUNT - counter);
	}
//-ZK/
	if(synchro != NULL)
		synchro->OnServerParam(&params);
	return 0;
}

#ifdef COACH // Spravy pre kouca 

// Parsuje spravu see_global (iba pre kouca)
// \param msg - sprava na parsovanie
// vracia 0 v pripade uspechu, inak chybu
int Parser::ParseSeeGlobal(const char* msg)
{
	static COACH_VISUAL_INFO info;
	memset(&info, 0, sizeof(COACH_VISUAL_INFO));

	static AGENT agentHelp;   // Pouziva sa iba v ParseSeePlayer(...)
	static LANDMARK ballHelp; // Pouziva sa iba v ParseSeeLandmark(...)

	static char item[256];
	static char seenObj[64];
	static char typeObj[32];

	static char tag1[32];
//	static char tag2[32];
//	static char tag3[32];

	int rval;
	const char* nextStr;
	const char* nextStr2;

	msg = ParseObj(msg, item);
	if(sscanf(item, "%d", &info.time) != 1)
		return 1;

/* Matus:
 * ak sa nemylim, tu maju bug, nevyhodnoti to posledneho hraca.	
 * sakra vsak to je cele divne, toto mam pocit ze nema nic spolocne so spravou see_global	
 */	
	msg = ParseBracket(msg, item);

	while(msg != NULL)
	{
		nextStr = ParseBracket(item, seenObj);
		if(!nextStr)
			return 1;

		nextStr2 = ParseObj(seenObj, typeObj);
		if(!nextStr2)
			return 1;

		switch(typeObj[0])
		{
			case 'p': // Hrac
				nextStr2 = ParseObj(nextStr2, tag1);
				
				if(nextStr2 != NULL)
				{
					if(strcmp(teamName, tag1) == 0) // Nas hrac
					{
						rval = ParseSeePlayer(nextStr2, nextStr, &agentHelp);
						if(rval != 0)
						{
							WARNN("error parsing player %s", item);
							return 1;
						}

// CHYBA?
						// Skopiruju sa pozadovane informacie z pomocnej premennej
						info.ours[info.oursCount].uniformNumber = agentHelp.uniformNumber;
						info.ours[info.oursCount].goalie = agentHelp.goalie;

						// Matus: toto vyzera divne, ked sa nastavuje X a Y suradnica zo
						// vzdialenosti a uhla... ale asi to je dobre (??).
						info.ours[info.oursCount].pos.SetX(agentHelp.dist);
						info.ours[info.oursCount].pos.SetY(agentHelp.dir);
						info.ours[info.oursCount].speed.SetX(agentHelp.distChange);
						info.ours[info.oursCount].speed.SetY(agentHelp.dirChange);
						info.ours[info.oursCount].bodyFacingDir = agentHelp.bodyFacingDir;
						info.ours[info.oursCount].headFacingDir = agentHelp.headFacingDir;
// CHYBA?/
						info.oursCount++;
					}
					else // Superov hrac
					{
						rval = ParseSeePlayer(nextStr2, nextStr, &agentHelp);
						if(rval != 0)
						{
							WARNN("error parsing player %s", item);
							return 1;
						}
// CHYBA?
						// Skopiruju sa pozadovane informacie z pomocnej premennej
						info.their[info.theirCount].uniformNumber = agentHelp.uniformNumber;
						info.their[info.theirCount].goalie = agentHelp.goalie;
						// Matus: toto vyzera divne, ked sa nastavuje X a Y suradnica zo
						// vzdialenosti a uhla... ale asi to je dobre (??).
						info.their[info.theirCount].pos.SetX(agentHelp.dist);
						info.their[info.theirCount].pos.SetY(agentHelp.dir);
						info.their[info.theirCount].speed.SetX(agentHelp.distChange);
						info.their[info.theirCount].speed.SetY(agentHelp.dirChange);
						info.their[info.theirCount].bodyFacingDir = agentHelp.bodyFacingDir;
						info.their[info.theirCount].headFacingDir = agentHelp.headFacingDir;
// CHYBA?/						
						info.theirCount++;
					}
				}
				break;

			case 'g': // Gol - kouc to nevyuziva
				break;
			
			case 'b': // Lopta
				rval = ParseSeeLandmark(nextStr, &ballHelp);
				if(rval != 0)
				{
					WARNN("error parsing ball: %s", item);
					return 1;
				}

				// Skopiruju sa pozadovane informacie z pomocnej premennej
// CHYBA?
				// Matus: toto vyzera divne, ked sa nastavuje X a Y suradnica zo
				// vzdialenosti a uhla... ale asi to je dobre (??).
				info.ball.pos.SetX(ballHelp.dist);
				info.ball.pos.SetY(ballHelp.dir);
				info.ball.speed.SetX(ballHelp.distChange);
				info.ball.speed.SetY(ballHelp.dirChange);
// CHYBA?/				
				break;

			default:
				WARNN("unknown object %s", typeObj);
				break;
		}

		msg = ParseBracket(msg, item);
	}

	if(synchro != NULL)
		synchro->OnSeeGlobal(&info);
	
	return 0;
}
#endif // COACH

#ifndef COACH

// Parsuje spravu see
// \param  msg - sprava na parsovanie
// vracia sa 0 v pripade uspechu, inak chyba
int Parser::ParseSee(const char* msg)
{
//	DBGN("%s", msg);

	static VISUAL_INFO info;
	memset(&info, 0, sizeof(VISUAL_INFO));

	static char item[256];
	static char seenObj[64];
	static char typeObj[32];

	static char tag1[32];

	int rval;
	const char* nextStr;
	const char* nextStr2;

	msg = ParseObj(msg, item);
	if(sscanf(item, "%d", &info.time) != 1)
		return 1;

	msg = ParseBracket(msg, item);

	while(msg)
	{
		nextStr = ParseBracket(item, seenObj);
		if(!nextStr)
			return 1;

		nextStr2 = ParseObj(seenObj, typeObj);
		if(!nextStr2)
			return 1;

		switch(typeObj[0])
		{
			case 'p': // Hrac
				nextStr2 = ParseObj(nextStr2, tag1);
				
				if(nextStr2 != NULL)
				{
					if(strcmp(teamName, tag1) == 0) // Nas hrac
					{
						rval = ParseSeePlayer(nextStr2, nextStr, &info.ours[info.oursCnt]);
						if(rval != 0)
						{
							WARNN("error parsing player %s", item);
							return 1;
						}
						
						//DBGN("see our player %i", info.ours[info.oursCnt].uniformNumber);
						info.oursCnt++;
					}
					else // Superov hrac
					{
						rval = ParseSeePlayer(nextStr2, nextStr, &info.their[info.theirCnt]);
						if(rval != 0)
						{
							WARNN("error parsing player %s", item);
							return 1;
						}
						
						info.theirCnt++;
					}
				}
				else // Neznamy hrac
				{
					rval = ParseSeePlayer("", nextStr, &info.unknown[info.unknownCnt]);
					if(rval != 0)
					{
						WARNN("error parsing player %s", item);
						return 1;
					}
						
					info.unknownCnt++;
				}

				break;

			case 'P': // Hrac blizko nas
				rval = ParseSeePlayer("", nextStr, &info.nearby[info.nearbyCnt]);
				if(rval != 0)
				{
					WARNN("error parsing player %s", item);
					return 1;
				}
						
				info.nearbyCnt++;
				break;

			case 'f': // Zastavka
				info.landmarks[info.landmarkCnt].type = ParseSeeFlagType(nextStr2);
				if(info.landmarks[info.landmarkCnt].type == NoLandmark)
				{
					WARNN("unknown flag type %s", nextStr2);
					return 1;
				}

				rval = ParseSeeLandmark(nextStr, &info.landmarks[info.landmarkCnt]);
				if(rval != 0)
				{
					WARNN("error parsing flag: %s", item);
					return 1;
				}

				info.landmarkCnt++;
				break;

			case 'F': // Zastavka blizko hraca (nepouziva sa)
				break;

			case 'g': // Branka
				if(!ParseObj(nextStr2, tag1))
					return 1;

				// Zisti sa typ branky
				if((tag1[0] == 'l' && realSide == LEFT) || (tag1[0] == 'r' && realSide == RIGHT))
					info.landmarks[info.landmarkCnt].type = GOAL_L;
				else if((tag1[0] == 'r' && realSide == LEFT) || (tag1[0] == 'l' && realSide == RIGHT))
					info.landmarks[info.landmarkCnt].type = GOAL_R;
				else
				{
					WARNN("unknown flag type %s", nextStr2);
					return 1;
				}

				// Parsuje sa jej pozicia
				rval = ParseSeeLandmark(nextStr, &info.landmarks[info.landmarkCnt]);
				
// Poznamka:	Nema tu byt != 1?
				if(rval != 0)
				{
					WARNN("error parsing goal: %s", item);
					return 1;
				}

				info.landmarkCnt++;
				break;
			
			case 'G': // Branka blizko nas (nepouziva sa)
				break;

			case 'b': // Lopta 
			case 'B': // Lopta blizko nas
				rval = ParseSeeLandmark(nextStr, &info.ball);
				info.ballCnt = 1;
				if(rval != 0)
				{
					WARNN("error parsing ball: %s", item);
					return 1;
				}

				break;

			case 'l': // Ciara
				if(!ParseObj(nextStr2, tag1))
					return 1;
				else if((tag1[0] == 't' && realSide == LEFT) || (tag1[0] == 'b' && realSide == RIGHT))
					info.lines[info.lineCnt].type = Top;
				else if((tag1[0] == 'b' && realSide == LEFT) || (tag1[0] == 't' && realSide == RIGHT))
					info.lines[info.lineCnt].type = Bottom;
				else if((tag1[0] == 'l' && realSide == LEFT) || (tag1[0] == 'r' && realSide == RIGHT))
					info.lines[info.lineCnt].type = Left;
				else if((tag1[0] == 'r' && realSide == LEFT) || (tag1[0] == 'l' && realSide == RIGHT))
					info.lines[info.lineCnt].type = Right;
				else return 1;

				rval = sscanf(nextStr, " %f %f", &info.lines[info.lineCnt].dist, &info.lines[info.lineCnt].dir);

				if(rval != 2)
				{
					WARNN("error parsing line: %s", item);
					return 1;
				}

				info.lineCnt++;
				break;
		
			default:
				WARNN("unknown object %s", typeObj);
				break;
		}

		msg = ParseBracket(msg, item);
	}

	if(synchro) synchro->OnSee(&info);
	return 0;
}

// Parsuje spravu sense_body
// \param  msg - sprava na parsovanie
// vracia sa 0 v pripade uspechu, inak chyba
int Parser::ParseSenseBody(const char* msg)
{
	static char item[128];
	static char itemType[64];
	static char tmp1[32], tmp2[32];
	static const char* nextStr;

	// POZOR presne tato instancia SENSE_BODY sa pouziva vselikde v programe (na konci tejto metody na nu
	// odovzdame pointer). Ak nebude static, cele sa to zblazni!
	static SENSE_BODY sb;
	memset(&sb, 0, sizeof(SENSE_BODY));

	// Cas
	msg = ParseObj(msg, item);
	if(sscanf(item, "%d", &sb.time) != 1)
		return 1;

	// Zvysok spravy
	msg = ParseBracket(msg, item);
	while(msg != NULL)
	{
		nextStr = ParseObj(item, itemType);
		if(nextStr == NULL)
			return 1;

		if(strcmp(itemType, "view_mode") == 0)
		{
			if(sscanf(nextStr, " %s %s", tmp1, tmp2) != 2)
				return 1;

			if(strcmp(tmp1, "high") == 0)
				sb.viewQuality = HIGH_QUALITY;
			else if(strcmp(tmp1, "low") == 0)
				sb.viewQuality = LOW_QUALITY;
			else 
				return 1;

			if(strcmp(tmp2, "narrow") == 0)
				sb.viewWidth = NARROW_VIEW;
			else if(strcmp(tmp2, "normal") == 0)
				sb.viewWidth = NORMAL_VIEW;
			else if(strcmp(tmp2, "wide") == 0)
				sb.viewWidth = WIDE_VIEW;
			else
				return 1;
		}
		else if(strcmp(itemType, "stamina") == 0)
		{
			if(sscanf(nextStr, " %f %f", &sb.stamina, &sb.effort) != 2)
				return 1;
		}
		else if(strcmp(itemType, "speed") == 0)
		{
			int rval = sscanf(nextStr, " %f %f", &sb.speedSize, &sb.speedDirection);
			if(rval != 2 && rval != 1)
				return 1;
		}
		else if(strcmp(itemType, "head_angle") == 0)
		{
			if(sscanf(nextStr, " %f", &sb.headAngle) != 1)
				return 1;
		}
		else if(strcmp(itemType, "tackle") == 0)
		{
			// (tackle (expires <EXPIRES>) (count <COUNT>))
			if(sscanf(nextStr, " ( expires %d ) ( count %d )", &sb.tackleExpires, &sb.counts.tackleCount) != 2)
				return 1;
		}
		else if(strcmp(itemType, "focus") == 0)
		{
			// (focus (target none) (count <COUNT>))
			// (focus (target {l|r} <UNUM>) (count <COUNT>)
			nextStr = ParseBracket(nextStr, tmp1);
			if(nextStr == NULL)
				return 1;

			if(strcmp(tmp1, "target none") == 0)
				sb.focusedNumber = 0;
			else
			{
				char side;
				if(sscanf(tmp1, " target %c %d", &side, &sb.focusedNumber) != 2)
					return 1;
				sb.focusedSide = (side == 'l' && realSide == LEFT) || (side == 'r' && realSide == RIGHT);
			}
			if(sscanf(nextStr, " ( count %d )", &sb.counts.attentiontoCount) != 1)
				return 1;
		}
		else if(strcmp(itemType, "arm") == 0)
		{
			// (arm (movable <MOVABLE>) (expires <EXPIRES>)
			// (target <DIST> <DIR>) (count <COUNT>))'
			if(sscanf(nextStr, " ( movable %d ) ( expires %d ) ( target %f %f ) ( count %d )",
				&sb.armMovableAfter, &sb.armExpires, &sb.armDistance, 
				&sb.armDirection, &sb.counts.pointtoCount) != 5)
				return 1;
		}
		else if(strcmp(itemType, "kick") == 0)
		{
			if(sscanf(nextStr, " %d", &sb.counts.kickCount) != 1)
				return 1;
		}
		else if(strcmp(itemType, "dash") == 0)
		{
			if(sscanf(nextStr, " %d", &sb.counts.dashCount) != 1)
				return 1;
		}
		else if(strcmp(itemType, "turn") == 0)
		{
			if(sscanf(nextStr, " %d", &sb.counts.turnCount) != 1)
				return 1;
		}
		else if(strcmp(itemType, "say") == 0)
		{
			if(sscanf(nextStr, " %d", &sb.counts.sayCount) != 1)
				return 1;
		}
		else if(strcmp(itemType, "turn_neck") == 0)
		{
			if(sscanf(nextStr, " %d", &sb.counts.turnNeckCount) != 1)
				return 1;
		}
		else if(strcmp(itemType, "catch") == 0)
		{
			if(sscanf(nextStr, " %d", &sb.counts.catchCount) != 1)
				return 1;
		}
		else if(strcmp(itemType, "move") == 0)
		{
			if(sscanf(nextStr, " %d", &sb.counts.moveCount) != 1)
				return 1;
		}
		else if(strcmp(itemType, "change_view") == 0)
		{
			if(sscanf(nextStr, " %d", &sb.counts.changeViewCount) != 1)
				return 1;
		}
		else
		{
			WARNN("unknown field in sense_body: %s", item);
		}

		msg = ParseBracket(msg, item);
	}

	if(synchro)
		synchro->OnSenseBody(&sb);

	return 0;
}
#endif // ifndef COACH


#ifndef COACH
// ---------------------------------- CLANG ----------------------------------

// Parsuje spravu obdrzanu zo servera. Tato metoda vola vsetky ostatne CLANG parsovacie metody.
// \param msg - sprava na parsovanie
// vracia sa 0 v pripade uspechu, inak chyba
int Parser::CLangParseMsg(const char* msg)
{
	char message[BUFFERSIZE];
	
	if(!ParseBracket(msg, message))
	{
		WARNN("message not starting with a bracket");
		return 1; // chyba
	}

	char typ[64];
	const char* nextStr = ParseObj(message, typ);

	if(nextStr == NULL)
		return 1; // chyba

	int success = 0;

    if(strcmp(typ, "freeform") == 0)
		success = CLangParseFreeform(nextStr);
    else if (strcmp(typ, "define") == 0)
	{
		WARNN("%s", msg);
		success = CLangParseDefine(nextStr);
	}
	else if(strcmp(typ, "error") == 0)
		WARNN("server reports \"%s\"", msg);
	else if(strcmp(typ, "warning") == 0)
		WARNN("server reports \"%s\"", msg);
	else if(strcmp(typ, "can't") == 0)
		WARNN("server reports \"%s\"", msg);
	else WARNN("not parsed: %s", msg);

	return success;
}

// Vyhodnocuje podmienky vo vstupnej sprave (zatial iba true a false)
int Parser::CLangEvaluateCondition(const char* msg)
{
    static char message[BUFFERSIZE];
    static char condition[BUFFERSIZE];

    if(!ParseBracket(msg, message))
	{
		WARNN("message not starting with a bracket");
		return false;
	}    

    const char* nextStr = ParseObj(message, condition);

   	if(nextStr == NULL)
		return false;

    if(strcmp(condition, "true") == 0)
		return true;
    else if (strcmp(condition, "false") == 0)
		return false;
   
	// Iba podmmienky true a false su vyhodnocovane. Ostatne, komplexnejsie podmienky nie su implementovane.
	else WARN("cannot evaluate condition");

    return false;
}

//
int Parser::CLangParseDefine(const char* msg)
{
    int success = 1;
    char message[BUFFERSIZE];

    if(!ParseBracket(msg, message))
	{
		WARNN("message not starting with a bracket");
		return success;	// chyba
	}    
    
	char typ[64];
	const char* nextStr = ParseObj(message, typ);

	if(nextStr == NULL)
		return success;	// chyba

    if(strcmp(typ, "definerule") == 0)
		success = CLangParseDefinerule(nextStr);

	return success;

}

//
int Parser::CLangParseDefinerule(const char* msg)
{
    int success = 1;
	char action[64];
    char rule[64];

    const char* nextStr = ParseObj(msg, action);

   	if(nextStr == NULL)
		return success;	// chyba

    nextStr = ParseObj(nextStr, rule);

    if(nextStr == NULL)
		return success;	// chyba

    if (strcmp(rule, "direc") == 0)
    {

      if(strcmp(action, "SetParameter") == 0) 
		  success = CLangParseSetParameter(nextStr);
      else if (strcmp(action, "ChangeFormation") == 0) 
		  success = CLangParseChangeFormation(nextStr);

      // Pridajte sem ostatne definovane akcie
    } else if ((strcmp(rule, "model") == 0))
    {
        // Nevyuziva sa, nie je to implementovane
    }

	return success;
}

//
int Parser::CLangParseFreeform(const char* msg)
{
    int success;
    static char message[BUFFERSIZE];

    success = CLangStripQuotes(msg, message);
    
    if(success != 0)
    {
       WARNN("freeform not parsed: %s ", msg);
       return success;
    }

    success = CLangParseMsg(message);

	return success;
}

//
int Parser::CLangParseChangeFormation(const char* msg)
{
    int success = 0;
    char message[BUFFERSIZE];
    char condition[BUFFERSIZE];
    char typeofaction[BUFFERSIZE];

    if(!ParseBracket(msg, message))
	{
		WARNN("message not starting with a bracket");
		return success;	// chyba
	}    

    const char* nextStr  = ParseObj(message, condition);

    if(nextStr == NULL)
		return success;	// chyba

    nextStr  = ParseObj(nextStr, typeofaction);   

    if(nextStr == NULL)
		return success;	// chyba

    if(!CLangEvaluateCondition(condition))
		return success; // chyba

    if(tactics)
		tactics->ChangeFormation(typeofaction);

	return 1;
}

//
int Parser::CLangParseSetParameter(const char* msg)
{
    int success = 1;
    static char message[BUFFERSIZE];
    static char condition[BUFFERSIZE];
    static char typeofaction[BUFFERSIZE];
    int delimiter = '=';

    if(!ParseBracket(msg, message))
	{
		WARNN("message not starting with a bracket");
		return success;	// chyba
	}    

    const char* nextStr  = ParseObj(message, condition);

    if(nextStr == NULL)
		return success;	// chyba   

    nextStr  = ParseObj(nextStr, typeofaction);   

    if(nextStr == NULL)
		return success; // chyba    

    if (!CLangEvaluateCondition(condition))
		return success; // chyba

    // get name=value pair
    static char name[100];
    char* value = strchr(typeofaction, delimiter);
    int pos = value - typeofaction;
    // Preskoc oddelovac
    value = value + 1;

    strncpy(name, typeofaction, pos);

	// Parsovanie pokracuje v Variables::ParseVariable
	if(memory->GetWorld()->variables->ParseVariable(name, value))
		success = 0;

	return success;
}

//
int Parser::CLangStripQuotes(const char* str, char* out)
{
	if(str == NULL || out == NULL)
		return -1;

	// Odstrani uvodne medzery
	while(*str == ' ')
		str++;
	if(*str == 0)
		return -2;

    // Nic na odstranenie 
    if(*str != '\"')
		return -3;

	if(*str == '\"')
	{
		str++;
		while(*str != '\"')
		{
			*out = *str;
			
			if(*str == '\0')
				return -3;
			out++;
			str++;
		}
		str++;
	}

	*out = '\0';

	return 0;
}

#endif // COACH
